#ifndef SIMPLEAMQPCLIENT_AMQPEXCEPTION_H
#define SIMPLEAMQPCLIENT_AMQPEXCEPTION_H
/*
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MIT
 *
 * Copyright (c) 2010-2013 Alan Antonuk
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * ***** END LICENSE BLOCK *****
 */

#include <stdint.h>
#include <stdexcept>
#include <string>

#include "Util.h"

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif

struct amqp_rpc_reply_t_;
struct amqp_channel_close_t_;
struct amqp_connection_close_t_;

namespace amqp {

    class  Exception : public std::runtime_error {
      public:
        static void Throw(const amqp_rpc_reply_t_ &reply);
        static void Throw(const amqp_channel_close_t_ &reply);
        static void Throw(const amqp_connection_close_t_ &reply);

        explicit Exception(const std::string &what, const std::string &reply_text,
                           uint16_t class_id,
                           uint16_t method_id) throw();
        virtual ~Exception() {}

        virtual bool is_soft_error() const throw() = 0;
        virtual uint16_t reply_code() const throw() = 0;
        virtual uint16_t class_id() const throw() { return m_class_id; }
        virtual uint16_t method_id() const throw() { return m_method_id; }
        virtual std::string reply_text() const throw() { return m_reply_text; }

      protected:
        std::string m_reply_text;
        uint16_t m_class_id;
        uint16_t m_method_id;
    };

    class  ConnectionException : public Exception {
      public:
        explicit ConnectionException(const std::string &what,
                                     const std::string &reply_text,
                                     uint16_t class_id,
                                     uint16_t method_id) throw()
            : Exception(what, reply_text, class_id, method_id) {}

        virtual bool is_soft_error() const throw() { return false; }
    };

    class  ChannelException : public Exception {
      public:
        explicit ChannelException(const std::string &what,
                                  const std::string &reply_text,
                                  uint16_t class_id,
                                  uint16_t method_id) throw()
            : Exception(what, reply_text, class_id, method_id) {}

        virtual bool is_soft_error() const throw() { return true; }
    };

    class  ConnectionForcedException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit ConnectionForcedException(const std::string &what,
                                           const std::string &reply_text,
                                           uint16_t class_id,
                                           uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  InvalidPathException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit InvalidPathException(const std::string &what,
                                      const std::string &reply_text,
                                      uint16_t class_id,
                                      uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  FrameErrorException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit FrameErrorException(const std::string &what,
                                     const std::string &reply_text,
                                     uint16_t class_id,
                                     uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  SyntaxErrorException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit SyntaxErrorException(const std::string &what,
                                      const std::string &reply_text,
                                      uint16_t class_id,
                                      uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  CommandInvalidException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit CommandInvalidException(const std::string &what,
                                         const std::string &reply_text,
                                         uint16_t class_id,
                                         uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  ChannelErrorException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit ChannelErrorException(const std::string &what,
                                       const std::string &reply_text,
                                       uint16_t class_id,
                                       uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  UnexpectedFrameException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit UnexpectedFrameException(const std::string &what,
                                          const std::string &reply_text,
                                          uint16_t class_id,
                                          uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  ResourceErrorException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit ResourceErrorException(const std::string &what,
                                        const std::string &reply_text,
                                        uint16_t class_id,
                                        uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  NotAllowedException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit NotAllowedException(const std::string &what,
                                     const std::string &reply_text,
                                     uint16_t class_id,
                                     uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  NotImplementedException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit NotImplementedException(const std::string &what,
                                         const std::string &reply_text,
                                         uint16_t class_id,
                                         uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  InternalErrorException : public ConnectionException {
      public:
        static const uint16_t REPLY_CODE;
        explicit InternalErrorException(const std::string &what,
                                        const std::string &reply_text,
                                        uint16_t class_id,
                                        uint16_t method_id) throw()
            : ConnectionException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  ContentTooLargeException : public ChannelException {
      public:
        static const uint16_t REPLY_CODE;
        explicit ContentTooLargeException(const std::string &what,
                                          const std::string reply_text,
                                          uint16_t class_id,
                                          uint16_t method_id) throw()
            : ChannelException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  NoRouteException : public ChannelException {
      public:
        static const uint16_t REPLY_CODE;
        explicit NoRouteException(const std::string &what,
                                  const std::string reply_text,
                                  uint16_t class_id,
                                  uint16_t method_id) throw()
            : ChannelException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  NoConsumersException : public ChannelException {
      public:
        static const uint16_t REPLY_CODE;
        explicit NoConsumersException(const std::string &what,
                                      const std::string reply_text,
                                      uint16_t class_id,
                                      uint16_t method_id) throw()
            : ChannelException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  AccessRefusedException : public ChannelException {
      public:
        static const uint16_t REPLY_CODE;
        explicit AccessRefusedException(const std::string &what,
                                        const std::string reply_text,
                                        uint16_t class_id,
                                        uint16_t method_id) throw()
            : ChannelException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  NotFoundException : public ChannelException {
      public:
        static const uint16_t REPLY_CODE;
        explicit NotFoundException(const std::string &what,
                                   const std::string reply_text,
                                   uint16_t class_id,
                                   uint16_t method_id) throw()
            : ChannelException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  ResourceLockedException : public ChannelException {
      public:
        static const uint16_t REPLY_CODE;
        explicit ResourceLockedException(const std::string &what,
                                         const std::string reply_text,
                                         uint16_t class_id,
                                         uint16_t method_id) throw()
            : ChannelException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

    class  PreconditionFailedException : public ChannelException {
      public:
        static const uint16_t REPLY_CODE;
        explicit PreconditionFailedException(const std::string &what,
                                             const std::string reply_text,
                                             uint16_t class_id,
                                             uint16_t method_id) throw()
            : ChannelException(what, reply_text, class_id, method_id) {}

        virtual uint16_t reply_code() const throw() { return REPLY_CODE; }
    };

////////////

    class  AmqpLibraryException : public std::runtime_error {
      public:
        static AmqpLibraryException CreateException(int error_code);
        static AmqpLibraryException CreateException(int error_code,
                const std::string& context);

        int ErrorCode() const { return m_errorCode; }

      protected:
        explicit AmqpLibraryException(const std::string& message,
                                      int error_code) throw();

      private:
        int m_errorCode;
    };


////

    class  AmqpResponseLibraryException : public std::runtime_error {
      public:
        static AmqpResponseLibraryException CreateException(
            const amqp_rpc_reply_t_& reply, const std::string& context);

      protected:
        explicit AmqpResponseLibraryException(const std::string& message) throw();
    };

///
    class BadUriException : public std::runtime_error {
      public:
        explicit BadUriException() : std::runtime_error("URI is malformed") {}
    };

    class ConnectionClosedException : public std::runtime_error {
      public:
        explicit ConnectionClosedException()
            : std::runtime_error("Connection is closed") {}
    };

    class ConsumerCancelledException : public std::runtime_error {
      public:
        explicit ConsumerCancelledException(const std::string& consumer_tag) throw()
            : std::runtime_error(
                  std::string("Consumer was cancelled: ").append(consumer_tag)),
              m_consumer_tag(consumer_tag) {}

        virtual ~ConsumerCancelledException() throw() {}

        std::string GetConsumerTag() const { return m_consumer_tag; }

      private:
        std::string m_consumer_tag;
    };

    class  ConsumerTagNotFoundException : public std::runtime_error {
      public:
        ConsumerTagNotFoundException() throw()
            : std::runtime_error("The specified consumer tag is unknown") {}
        virtual ~ConsumerTagNotFoundException() throw() {}
    };

    class Message;
    typedef std::shared_ptr<Message> MessagePtr;

    class  MessageReturnedException : public std::runtime_error {
      public:
        explicit MessageReturnedException(MessagePtr message,
                                          uint32_t reply_code,
                                          const std::string& reply_text,
                                          const std::string& exchange,
                                          const std::string& routing_key) throw();

        virtual ~MessageReturnedException() {}

        MessagePtr message() const throw() { return m_message; }
        uint32_t reply_code() const throw() { return m_reply_code; }
        std::string reply_text() const throw() { return m_reply_text; }
        std::string exchange() const throw() { return m_exchange; }
        std::string routing_key() const throw() { return m_routing_key; }

      private:
        MessagePtr m_message;
        uint32_t m_reply_code;
        std::string m_reply_text;
        std::string m_exchange;
        std::string m_routing_key;
        mutable std::string m_what;
    };
}  // namespace amqp

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#endif  // SIMPLEAMQPCLIENT_AMQPEXCEPTION_H
